Skip to content
lab components / Data Visualization

Chart

Present users with interactive graphical representations of data.

This is a Lab component!

That means it doesn't satisfy our definition of done and may be changed or even deleted. For an exact status, please reach out to the Fancy team through the dev_fancy or ux_fancy channels.

import { Chart } from "@siteimprove/fancylab";

#Intro

The Chart component is a tailored wrapper built for the Highcharts library within a React environment. It's designed to offer a seamless integration of Highcharts while incorporating customized features, improved accessibility, and enhanced usability.

#Key Features

  • Customizations: The Chart component provides predefined customizations regarding colors, sizes, and themes to maintain consistency and visual appeal across charts.
  • Accessibility Improvements: Accessibility is a prime focus. The component ensures that charts are optimized for screen readers and other assistive technologies, making data visualization more inclusive.
  • Enhanced Usability: With improved interaction elements and intuitive functionalities, the Chart component aims to enhance user experience and ease of navigation within the charts.
  • Highcharts Features: Developers have full access to all Highcharts features, allowing them to leverage the extensive capabilities of Highcharts to create sophisticated and detailed charts.

#Support and Troubleshooting

The Chart component strives to ensure a seamless experience with Highcharts integrations. If you encounter any issues or discrepancies specific to the Chart component that differ from the standard Highcharts behavior, please reach out to us for support. If the issue works as expected with Highcharts in its raw form, kindly let us know to address the compatibility concern.

#Examples

The examples below showcase the various kinds of charts suported by Highcharts, such as line charts, column charts, and more (coming soon). Click on the tabs to view the examples.

For general features and functionalities, please refer to the Miscellaneous section.



#Basic usage

<Chart options={{ series: [ { type: "line", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue1", }; }), }, { type: "line", name: "Series 2", data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue2", }; }), }, ], }} />

#Usage with categories

<Chart options={{ xAxis: { categories: ["Apples", "Bananas", "Oranges", "Strawberries"], tickInterval: 1, }, series: [ { type: "line", name: "Series 1", data: [25, 23, 48, 52], }, { type: "line", name: "Series 2", data: [18, 26, 36, 44], }, ], }} />

#Usage with X axis showing time

<Chart options={{ xAxis: { type: "datetime", // Set interval of tick marks to one month. For datetime axes this is based on milliseconds. tickInterval: 24 * 3600 * 1000 * 30, }, series: [ { type: "line", name: "Series 1", dashStyle: "ShortDot", data: [ { x: Date.UTC(2010, 0, 1), y: 10, }, { x: Date.UTC(2010, 1, 2), y: 20, }, { x: Date.UTC(2010, 2, 3), y: 30, }, { x: Date.UTC(2010, 3, 4), y: 40, }, { x: Date.UTC(2010, 4, 5), y: 50, }, ], }, { type: "line", name: "Series 2", data: [ { x: Date.UTC(2010, 0, 1), y: 20, }, { x: Date.UTC(2010, 1, 2), y: 30, }, { x: Date.UTC(2010, 2, 3), y: 40, }, { x: Date.UTC(2010, 3, 4), y: 50, }, { x: Date.UTC(2010, 5, 5), y: 60, }, ], }, ], }} />

#Usage with multiple Y axes

<Chart options={{ xAxis: [ { type: "datetime", // Set interval of tick marks to one month. For datetime axes this is based on milliseconds. tickInterval: 24 * 3600 * 1000 * 30, }, ], yAxis: [{}, { opposite: true }], series: [ { type: "line", name: "Series 1", data: [ { x: Date.UTC(2010, 0, 1), y: 10, }, { x: Date.UTC(2010, 1, 1), y: 20, }, { x: Date.UTC(2010, 2, 1), y: 30, }, { x: Date.UTC(2010, 3, 1), y: 40, }, { x: Date.UTC(2010, 4, 1), y: 50, }, ], }, { type: "line", name: "Series 2", data: [ { x: Date.UTC(2010, 0, 1), y: 20, }, { x: Date.UTC(2010, 1, 1), y: 30, }, { x: Date.UTC(2010, 2, 1), y: 40, }, { x: Date.UTC(2010, 3, 1), y: 50, }, { x: Date.UTC(2010, 5, 1), y: 60, }, ], }, { type: "line", name: "Series 3", data: [ { x: Date.UTC(2010, 0, 1), y: 25, }, { x: Date.UTC(2010, 1, 1), y: 50, }, { x: Date.UTC(2010, 2, 1), y: 75, }, { x: Date.UTC(2010, 3, 1), y: 90, }, { x: Date.UTC(2010, 5, 1), y: 105, }, ], yAxis: 1, }, ], }} />

#Usage with several series

<Chart options={{ series: [ { type: "line", name: "Series 1", data: [1.12, 1.14, 1.13, 1.16, 1.18, 1.17, 1.15, 1.19, 1.2, 1.22].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 2", data: [1.01, 1.05, 1.1, 1.09, 1.05, 1.02, 1.0, 1.01, 1.01, 1.02].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 3", data: [0.9, 1.01, 1.02, 1.03, 1.02, 1.0, 0.98, 0.96, 0.95, 0.99].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 4", data: [1.5, 1.6, 1.65, 1.7, 1.75, 1.78, 1.78, 1.79, 1.8, 1.8].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 5", data: [1.4, 1.38, 1.35, 1.31, 1.3, 1.3, 1.29, 1.28, 1.3, 1.32].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 6", data: [1.0, 1.1, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, ], }} />

#Usage with Annotations

Annotations are designed to be easily adaptable to any backend. For that, you can map your own data structure to our Annotations interface which has the following definition:

type AnnotationItem = { id: string | number; date: Date; comment: string; createdBy: string; visibility: string; isFavorite: boolean; visibleIn: string[]; username: string; }

In addition, the Chart component also provides properties for handling all basic annotation operations, such as creation (onCreateAnnotation), update (onUpdateAnnotation), and deletion (onDeleteAnnotation).

In the example below, the API object simulates the implementation of these methods and also provides a list of annotations according to the AnnotationItem interface.

Show:

const chartDateOptions: DateOptions = { locale: "en-us", skipTimezoneReset: false, }; const [annotationsData, setAnnotationsData] = useState<AnnotationItem[]>([]); const [annotationsAreasVisibleIn, setAnnotationsAreasVisibleIn] = useState< AnnotationAreasVisibleIn[] >([]); useEffect(() => { let isDataFetchDone = true; API.fetch().then((data) => isDataFetchDone && setAnnotationsData(data)); API.fetchAreasVisibleIn().then( (areas) => isDataFetchDone && setAnnotationsAreasVisibleIn(areas) ); return () => { isDataFetchDone = false; }; }, []); return ( <Chart options={{ xAxis: { type: "datetime", // Set interval of tick marks to one month. For datetime axes this is based on milliseconds. tickInterval: 24 * 3600 * 1000 * 30, }, series: [ { type: "line", name: "Series 1", data: [ { x: Date.UTC(2022, 1, 1), y: 20, }, { x: Date.UTC(2022, 2, 1), y: 30, }, { x: Date.UTC(2022, 3, 1), y: 40, }, { x: Date.UTC(2022, 4, 1), y: 50, }, { x: Date.UTC(2022, 5, 1), y: 30, }, ], }, { type: "line", name: "Series 2", data: [ { x: Date.UTC(2022, 0, 1), y: 20, }, { x: Date.UTC(2022, 1, 1), y: 30, }, { x: Date.UTC(2022, 2, 1), y: 40, }, { x: Date.UTC(2022, 3, 1), y: 50, }, { x: Date.UTC(2022, 5, 1), y: 60, }, ], }, ], }} showAnnotationButton annotations={annotationsData} chartPeriod={[new Date(2022, 0, 1), new Date(2022, 3, 1)]} onUpdateAnnotation={async (data: Omit<Partial<AnnotationItem>, "createdBy">) => { await API.update(data); setAnnotationsData(await API.fetch()); }} onCreateAnnotation={async (data: Omit<AnnotationItem, "id" | "createdBy">) => { await API.create(data); setAnnotationsData(await API.fetch()); }} onDeleteAnnotation={async (id: string | number) => { await API.delete(id); setAnnotationsData(await API.fetch()); }} chartDateOptions={chartDateOptions} annotationAreasVisibleIn={annotationsAreasVisibleIn} /> );

#Miscellaneous

These examples showcase the various features and functionalities of the Chart component that are not specific to any particular chart type. It includes features such as legends, tooltips, sizes, and more.

#Usage with reference lines

const { ColorRed, ColorTextStaticBody } = useDesignToken(); return ( <Chart options={{ yAxis: { plotLines: [ { value: 70, color: ColorRed, width: 3, dashStyle: "ShortDot", label: { text: "Limit (70)", align: "right", x: -5, style: { color: ColorTextStaticBody, }, }, }, ], }, series: [ { type: "line", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 2", data: [15, 13, 38, 42, 42, 47, 46, 51, 56].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, ], }} /> );

#Usage with no legend

<Chart options={{ plotOptions: { series: { showInLegend: false, }, }, series: [ { type: "column", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue1", }; }), }, { type: "line", name: "Series 2", data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue2", }; }), }, ], }} />

#Usage with series hidden on load

<Chart options={{ series: [ { type: "column", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { type: "line", name: "Series 2", data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), selected: false, }, ], }} />

#Usage with series not visible in the legends

<Chart options={{ series: [ { type: "column", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, { showInLegend: false, type: "line", name: "Series 2", data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: i.toString(), }; }), }, ], }} />

#Usage with missing data

If all series contain empty data (undefined, null or NaN) in a determined position, the row will not be displayed on the table visualization.

<Chart options={{ series: [ { type: "column", name: "Series 1", data: [25, 23, 48, 52, undefined, 52, 57, 56, 61].map((val, i) => { return { x: i, y: val, }; }), }, { type: "line", name: "Series 2", data: [18, 26, 36, 44, undefined, 49, 59, 65, 71].map((val, i) => { return { x: i, y: val, }; }), }, ], }} />

#Usage with tooltip formatters

The header of the chart tooltip can be formatted using the optional property tooltipHeaderFormatter with the values default, named, and keyed.

The individual points of the chart tooltip can be formatted using the optional property tooltipPointFormatter with the values default, named, and customValue.

<Chart options={{ series: [ { type: "column", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: "Name for x: " + i.toString(), customValue: "Custom value for x: " + i.toString() + " in series 1", }; }), }, { type: "line", name: "Series 2", data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: "Name for x: " + i.toString(), customValue: "Custom value for x: " + i.toString() + " in series 2", }; }), }, ], }} tooltipHeaderFormatter="named" tooltipPointFormatter="customValue" />

#Visual

#Height

Use the height property to adjust the chart size. The default height is medium (310px) and should be used in most cases. Use small height when space is limited. Use large or a custom value when the previous sizes are not suitable.

function BaseChart(props: { height: ChartProps["height"] }) { return ( <> <InlineText emphasis="medium">{props.height}</InlineText> <br /> <Chart height={props.height} hideContextMenu options={{ series: [ { type: "line", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: val.toString(), }; }), }, ], }} /> </> ); } return ( <> <BaseChart height="small" /> <BaseChart height="medium" /> <BaseChart height="large" /> </> );

#Usage with context menu changes

Use the hideContextMenu property to remove the Context Menu gear icon. The default setting is to show the Context Menu. In addition, use the hideEnhancedContrastOption property to remove the Enhanced contrast option from the Conext menu when that is visible. The default setting is to show the Enhanced Contrast option.

In the example below, the hideContextMenu is disabled and the hideEnhancedContrastOption is enabled.

<Chart hideContextMenu={false} hideEnhancedContrastOption options={{ series: [ { type: "column", name: "Series 1", data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue1", }; }), }, { type: "line", name: "Series 2", data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue2", }; }), }, ], }} />

#Usage with persistent legend state

Use the onToggleSeries property to persist the legend state. The default setting is to not persist the legend state, which means that the legend state will be reset when the page is refreshed. The visibility state of each series is controlled by the visible property of the series object.

const [series, setSeries] = useState<Highcharts.SeriesOptionsType[]>([ { type: "column", name: "Series 1", visible: true, data: [25, 23, 48, 52, 52, 57, 56, 61, 66].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue1", }; }), }, { type: "line", name: "Series 2", visible: false, data: [18, 26, 36, 44, 49, 59, 65, 71, 82].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue2", }; }), }, ]); return ( <Chart onToggleSeries={(seriesIndex, visible) => { const newSeries = [...series]; newSeries[seriesIndex].visible = visible; setSeries(newSeries); console.log( "Save series visibility to user preferences (e.g.: localStorage)", seriesIndex, visible ); }} options={{ series }} /> );

#Data updates

The Chart component automatically updates the chart when the options or height properties change. This is useful when the chart data is updated dynamically or the layout changes and the chart needs to be redrawn.

const numberOfSeries = 2; const seriesLength = 10; const buildData = () => generateRandomData(numberOfSeries, seriesLength); const [series, setSeries] = useState<number[][]>(buildData()); return ( <Content> <Button onClick={() => setSeries(buildData())}>Generate series</Button> <Chart options={{ series: [ { type: "column", name: "Series 1", data: series[0].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue1", }; }), }, { type: "line", name: "Series 2", data: series[1].map((val, i) => { return { x: i, y: val, name: i.toString(), customValue: "customValue2", }; }), }, ], }} /> </Content> );

#Usage with no data

In some real-world scenarios, the chart may not have any data to display. In this case, the chart frame and the table view will be empty.

<Chart options={{ series: [], }} />

#Properties

PropertyDescriptionDefinedValue
optionsRequired
objectOptions for Highcharts chart
showAnnotationButtonOptional
booleanShould show annotation button
annotationsOptional
object[]Annotations array
chartPeriodOptional
date[]Chart Period (for Annotations)
onUpdateAnnotationOptional
functionPUT call to update Annotation
onCreateAnnotationOptional
functionCREATE new Annotation
onDeleteAnnotationOptional
functionDELETE new Annotation
chartDateOptionsOptional
objectDate Options for the chart annotations
annotationAreasVisibleInOptional
object[]Array of areas to allow annotations to be visible in
heightOptional
| string | numberChart height (default medium, i.e. 310px)
containerClassNameOptional
stringCSS class for the Highcharts wrapper
useHighchartsLegendOptional
booleanUse native Highcharts legend
screenReaderRegionLabelOptional
stringLabel for screen reader region
screenReaderChartHeadingOptional
stringLabel for chart
defaultChartTitleOptional
stringDefault chart label
tableSummaryOptional
stringSummary for the data displayed in the data table
chartContainerLabelOptional
stringLabel for chart container
legendItemOptional
stringLegend item label
onToggleSeriesOptional
functionCallback for toggling series visibility
chartTypesMapTypeDescriptionOptional
stringDescription for chart type map type
chartTypesCombinationChartOptional
stringDescription for chart type combination chart
chartTypesDefaultSingleOptional
stringDescription for chart type default single
chartTypesDefaultMultipleOptional
stringDescription for chart type default multiple
chartTypesSplineSingleOptional
stringDescription for chart type spline single
chartTypesSplineMultipleOptional
stringDescription for chart type spline multiple
chartTypesLineSingleOptional
stringDescription for chart type line single
chartTypesLineMultipleOptional
stringDescription for chart type line multiple
chartTypesColumnSingleOptional
stringDescription for chart type column single
chartTypesColumnMultipleOptional
stringDescription for chart type column multiple
chartTypesBarSingleOptional
stringDescription for chart type bar single
chartTypesBarMultipleOptional
stringDescription for chart type bar multiple
chartTypesPieSingleOptional
stringDescription for chart type pie single
chartTypesPieMultipleOptional
stringDescription for chart type pie multiple
xAxisDescriptionSingularOptional
stringDescription for x-axis singluar
xAxisDescriptionPluralOptional
stringDescription for x-axis plural
categoryColumnHeaderOptional
stringTable column header for x-axis when the type is "category", "linear", or "logarithmic"
datetimeColumnHeaderOptional
stringTable column header for x-axis when the type is "datetime"
yAxisDescriptionSingularOptional
stringDescription for y-axis singluar
yAxisDescriptionPluralOptional
stringDescription for y-axis plural
chartSeriesDefaultOptional
stringChart series summary default
chartSeriesDefaultCombinationOptional
stringChart series summary default combination
chartSeriesLineOptional
stringChart series summary line
chartSeriesLineCombinationOptional
stringChart series summary line combination
chartSeriesSplineOptional
stringChart series summary spline
chartSeriesSplineCombinationOptional
stringChart series summary spline combination
chartSeriesColumnOptional
stringChart series summary column
chartSeriesColumnCombinationOptional
stringChart series summary column combination
chartSeriesBarOptional
stringChart series summary bar
chartSeriesBarCombinationOptional
stringChart series summary bar combination
chartSeriesPieOptional
stringChart series summary pie
chartSeriesScatterCombinationOptional
stringChart series summary pie combination
chartSeriesMapCombinationOptional
stringChart series summary map combination
chartSeriesMapbubbleCombinationOptional
stringChart series summary mapbubble combination
xAxisDescriptionOptional
stringDescription of x-axis
yAxisDescriptionOptional
stringDescription of y-axis
contextMenuForLabelOptional
stringLabel for the context menu. It will be appended with the chart title.
contextMenuEnhancedContrastLabelOptional
stringLabel for the enhanced contrast button in the context menu
contextMenuEnhancedContrastTooltipOptional
stringTooltip content for the enhanced contrast button
contextMenuTableViewLabelOptional
stringLabel for table view button in the context menu
contextMenuTableViewTooltipOptional
stringTooltip content for the table view button
hideContextMenuOptional
booleanHides the context menu icon
hideEnhancedContrastOptionOptional
booleanHides the enhanced contrast context option
overflowableOptional
booleanShould overflow be allowed (if not, tooltip might be cut off)
a11yModeOptional
booleanEnables enhanced contrast mode
tooltipHeaderFormatterOptional
"default" | "keyed" | "named"Tooltip header formatters
tooltipPointFormatterOptional
"customValue" | "default" | "named"Tooltip point formatters
data-observe-keyOptional
stringUnique string, used by external script e.g. for event tracking
classNameOptional
stringCustom className that's applied to the outermost element (only intended for special cases)
styleOptional
objectStyle object to apply custom inline styles (only intended for special cases)

#Guidelines

#Best practices

  • ...
  • ...

#Do not use when

  • ...
  • ...

#Accessibility

This component comes with built-in accessibility, no extra work required.

Explore detailed guidelines for this component: Accessibility Specifications

#Writing

  • ...
  • ...